package com.dianrong.common.uniauth.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ReflectionUtils {
	private ReflectionUtils() {
	}

	public static Object getField(Object targetObj, String fieldName, boolean isParentField) {
		Object object = null;
		try {
			Class<?> targetClazz = targetObj.getClass();
			Field field = null;
			if (isParentField) {
				field = targetClazz.getSuperclass().getDeclaredField(fieldName);
			} else {
				field = targetClazz.getDeclaredField(fieldName);
			}
			field.setAccessible(true);
			object = field.get(targetObj);
		} catch (Exception e) {
			log.warn("exception", e);
		}
		return object;
	}

	public static Object getField(Object targetObj, String fieldName) {
		Object object = null;
		try {
			Class<?> targetClazz = targetObj.getClass();
			Field field = null;
			while (targetClazz != null) {
				try {
					field = targetClazz.getDeclaredField(fieldName);
				} catch (NoSuchFieldException ex) {
					log.debug(targetClazz.getName() + " can not find field " + fieldName, ex);
				}
				// find it
				if (field != null) {
					break;
				}
				targetClazz = targetClazz.getSuperclass();
			}
			if (field == null) {
				throw new NoSuchFieldException(fieldName);
			}
			field.setAccessible(true);
			object = field.get(targetObj);
		} catch (Exception e) {
			log.warn("exception", e);
		}
		return object;
	}

	public static Object invokeStaticMethodWithoutParam(Class<?> clazz, String methodName) {
		Object object = null;
		try {
			Method method = clazz.getMethod(methodName, new Class[0]);
			object = method.invoke(null, new Object[0]);
		} catch (Exception e) {
			log.warn("exception", e);
		}
		return object;
	}

	public static Object invokeMethodWithoutParam(Object targetObj, String methodName) {
		Object object = null;
		try {
			Method method = targetObj.getClass().getMethod(methodName, new Class[0]);
			object = method.invoke(targetObj, new Object[0]);
		} catch (Exception e) {
			log.warn(e.getMessage());
		}
		return object;
	}

	public static void setUserInfoField(Object targetObj, String fieldName, Object fieldValue) {
		Field field = null;
		Class<?> selfClazz = targetObj.getClass();
		while (field == null) {
			try {
				field = selfClazz.getDeclaredField(fieldName);
			} catch (Exception e) {
				log.debug("exception", e);
				selfClazz = selfClazz.getSuperclass();
			}
		}
		field.setAccessible(true);
		try {
			field.set(targetObj, fieldValue);
		} catch (Exception e) {
			log.warn(e.getMessage());
		}
	}

	public static void setStaticField(String clazzName, String fieldName, Object fieldValue) {
		try {
			Class<?> clazz = Class.forName(clazzName);
			Field field = clazz.getDeclaredField(fieldName);
			field.setAccessible(true);
			field.set(null, fieldValue);
		} catch (Exception e) {
			log.warn(e.getMessage());
		}
	}

	public static Object getStaticField(String clazzName, String fieldName) {
		Object object = null;
		try {
			Class<?> clazz = Class.forName(clazzName);
			Field field = clazz.getDeclaredField(fieldName);
			field.setAccessible(true);
			object = field.get(null);
		} catch (Exception e) {
			log.warn(e.getMessage());
		}
		return object;
	}

	public static Long getOpUserId() {
		Long opUserId = null;
		// SecurityContextHolder.getContext().getAuthentication().getPrincipal()
		try {
			Class<?> clazz = Class.forName("org.springframework.security.core.context.SecurityContextHolder");
			if (clazz != null) {
				Object securityContext = ReflectionUtils.invokeStaticMethodWithoutParam(clazz, "getContext");
				if (securityContext != null) {
					Object authentication = ReflectionUtils.invokeMethodWithoutParam(securityContext,
							"getAuthentication");
					if (authentication != null) {
						Object principal = ReflectionUtils.invokeMethodWithoutParam(authentication, "getPrincipal");
						if (principal != null) {
							opUserId = (Long) ReflectionUtils.invokeMethodWithoutParam(principal, "getId");
						}
					}
				}
			}
		} catch (ClassNotFoundException e) {
			log.debug(
					"class <org.springframework.security.core.context.SecurityContextHolder> not found, maybe getOpUserId() called on the uniauth-server side?");
		}
		return opUserId;
	}

	public static Class<?> getSuperClassGenricType(Class<?> clazz, int index) {
		Type genType = clazz.getGenericSuperclass();// 得到泛型父类
		// 如果没有实现ParameterizedType接口，即不支持泛型，直接返回Object.class
		if (!(genType instanceof ParameterizedType)) {
			return Object.class;
		}
		// 返回表示此类型实际类型参数的Type对象的数组,数组里放的都是对应类型的Class, 如BuyerServiceBean extends
		// DaoSupport<Buyer,Contact>就返回Buyer和Contact类型
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		if (index >= params.length || index < 0) {
			throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数"));
		}
		if (!(params[index] instanceof Class)) {
			return Object.class;
		}
		return (Class<?>) params[index];
	}

	/**
	 * 通过反射,获得指定类的父类的第一个泛型参数的实际类型. 如DaoSupport<Buyer>
	 * 
	 * @param clazz
	 *            clazz 需要反射的类,该类必须继承泛型父类
	 * @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回
	 *         <code>Object.class</code>
	 */
	public static Class<?> getSuperClassGenricType(Class<?> clazz) {
		return getSuperClassGenricType(clazz, 0);
	}
}
